package cn.ywang.spring.customized.framework.webmvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Map;

/**
 * 参数匹配器
 *
 * @author BeatificWang
 * @date 2018/11/7 21:52
 */
public class HandlerAdapter {

	private Map<String, Integer> paramMapping;

	public HandlerAdapter(Map<String, Integer> paramMapping) {
		this.paramMapping = paramMapping;
	}

	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, HandlerMapping handler) throws Exception {
		// 根据用户请求的参数信息，跟method中的参数信息进行动态匹配
		// resp传进来的目的：将其赋值给方法参数
		// 只有当用户传进来的ModelAndView为空的时候，才会new 一个默认的

		// 1. 要准备好这个方法的形参列表

		// 方法重载:形参个数、类型、顺序、方法名
		Class<?>[] paramTypes = handler.getMethod().getParameterTypes();

		// 2. 拿到自定义命名参数所在的位置
		// 用户通过URL传过来的参数列表
		Map<String, String[]> reqParameterMap = request.getParameterMap();

		// 3. 构造实参列表
		Object[] paramValues = new Object[paramTypes.length];
		for (Map.Entry<String, String[]> entry : reqParameterMap.entrySet()) {
			String value = Arrays.toString(entry.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s", "");
			if (!this.paramMapping.containsKey(entry.getKey())) {
				continue;
			}
			int index = this.paramMapping.get(entry.getKey());
			//因为页面上传过来的值都是String类型的，而在方法中定义的类型是千变万化的
			//要针对我们传过来的参数进行类型转换
			paramValues[index] = this.caseStringValue(value, paramTypes[index]);
		}

		if (this.paramMapping.containsKey(HttpServletRequest.class.getName())) {
			int reqIndex = this.paramMapping.get(HttpServletRequest.class.getName());
			paramValues[reqIndex] = request;
		}

		if (this.paramMapping.containsKey(HttpServletResponse.class.getName())) {
			int respIndex = this.paramMapping.get(HttpServletResponse.class.getName());
			paramValues[respIndex] = response;
		}

		//4、从handler中取出controller、method，然后利用反射机制进行调用

		Object result = handler.getMethod().invoke(handler.getController(), paramValues);

		if (result == null) {
			return null;
		}

		boolean isModelAndView = handler.getMethod().getReturnType() == ModelAndView.class;
		if (isModelAndView) {
			return (ModelAndView) result;
		}
		else {
			return null;
		}
	}

	private Object caseStringValue(String value, Class<?> clazz) {
		if (clazz == String.class) {
			return value;
		}
		else if (clazz == Integer.class) {
			return Integer.valueOf(value);
		}
		else if (clazz == int.class) {
			return Integer.valueOf(value).intValue();
		}
		else {
			return null;
		}
	}
}